package net.sf.calibur.engine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.calibur.engine.task.Handler;
import net.sf.calibur.engine.task.Task;
import net.sf.calibur.engine.task.TaskInstance;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExecutorPool {
	private String name;
	private int size;
	private EventRouter router;
	private List<Executor> allExecutors = new ArrayList<Executor>();
	private Map<Object, List<Task>> tasksMap = new HashMap<Object, List<Task>>();
	private Logger logger = LoggerFactory.getLogger(getClass());
	private CaliburEngine engine;

	public ExecutorPool(CaliburEngine engine, String name, int size,
			EventRouter router, Handler... groups) {
		this.engine = engine;
		this.name = name;
		this.size = size;
		this.router = router;

		for (int i = 0; i < size; ++i) {
			allExecutors.add(new Executor(this, i));
		}

		Map<Object, Task> tasks = null;
		for (Handler g : groups) {
			tasks = g.getAllTasks();
			for (Object eventId : tasks.keySet()) {
				List<Task> list = tasksMap.get(eventId);
				if (list == null) {
					list = new ArrayList<Task>();
					tasksMap.put(eventId, list);
				}
				list.add(tasks.get(eventId));
			}
		}
	}

	public int getSize() {
		return size;
	}

	public CaliburEngine getEngine() {
		return engine;
	}

	public int getResponseTasksNum(Event event) {
		List<Task> list = tasksMap.get(event.getId());
		return list == null ? 0 : list.size();
	}

	public void onEvent(Event event) {
		List<Task> list = tasksMap.get(event.getId());
		if (list == null) {
			return;
		}

		Executor executor = null;
		for (Task task : list) {
			executor = null;
			try {
				executor = router.route(event, this);
			} catch (Throwable t) {
				logger.error(String.format(
						"ExecutorPool %s route event %s error.", name,
						event.toString()), t);
				continue;
			}

			if (executor == null) {
				logger.error(String.format(
						"ExecutorPool %s route event %s error.", name,
						event.toString()));
				continue;
			}

			try {
				TaskInstance taskInstance = new TaskInstance(task, event);
				executor.addTask(taskInstance);
			} catch (Throwable t) {
				logger.error("Executor pool proecss event fail.", t);
			}
		}
	}

	public void start() {
		for (Executor r : allExecutors) {
			if (r.getStatus() == Executor.Status.stopped) {
				r.start();
			}
		}
	}

	public void shutdown() {
		for (Executor r : allExecutors) {
			r.shutdown();
		}
	}

	public String getName() {
		return name;
	}

	public Executor getExecutor(int idx) {
		return allExecutors.get(idx);
	}

	public Executor getMinLoadExecutor() {
		int load = Integer.MAX_VALUE;
		int idx = 0;
		int waiting = 0;
		for (int i = 0; i < allExecutors.size(); ++i) {
			waiting = allExecutors.get(i).getWaitingEventNum();
			if (waiting < load) {
				load = waiting;
				idx = i;
			}
		}

		return allExecutors.get(idx);
	}
}
